{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "db1ab62b-7595-4b1e-9f2e-c0eafb670948",
   "metadata": {},
   "source": [
    "## 1. 字符串模板 PromptTemplate，单纯的字符串"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "6a598666-13dc-4d96-8193-6358d2133cdf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'你是一个起名大师，帮我起一个具有法国特色的男孩名字'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "prompt = PromptTemplate.from_template(\"你是一个起名大师，帮我起一个具有{country}特色的{sex}孩名字\")\n",
    "prompt.format(country=\"法国\", sex=\"男\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c20cf3f4-c95b-45e9-ba9a-69146edadc72",
   "metadata": {},
   "source": [
    "## 2. 对话模板 ChatPromptTemplate，同时构建出多个角色的对话消息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "7f5f6838-8295-4c3b-a8ca-c032db0f93ed",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[SystemMessage(content='你是一个起名大师，你的名字叫陈大师', additional_kwargs={}, response_metadata={}),\n",
       " HumanMessage(content='你好陈大师, 你感觉如何', additional_kwargs={}, response_metadata={}),\n",
       " AIMessage(content='你好，我状态很好', additional_kwargs={}, response_metadata={}),\n",
       " HumanMessage(content='你叫什么名字', additional_kwargs={}, response_metadata={})]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.prompts import ChatPromptTemplate\n",
    "\n",
    "chat_prompt = ChatPromptTemplate.from_messages([\n",
    "    (\"system\", \"你是一个起名大师，你的名字叫{name}\"),\n",
    "    (\"human\", \"你好{name}, 你感觉如何\"),\n",
    "    (\"ai\", \"你好，我状态很好\"),\n",
    "    (\"human\", \"{user_input}\"),\n",
    "])\n",
    "\n",
    "chat_prompt.format_messages(name=\"陈大师\", user_input=\"你叫什么名字\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "932e972f-ea70-4d54-82bf-ca24c1655123",
   "metadata": {},
   "source": [
    "### schema 包也可构建具体角色的消息"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "25e4de45-b0bd-4828-bff8-93e07556baec",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[SystemMessage(content='你是一个起名大师', additional_kwargs={'大师姓名': '陈瞎子'}, response_metadata={}),\n",
       " HumanMessage(content='请问大师叫什么？', additional_kwargs={}, response_metadata={}),\n",
       " AIMessage(content='我叫陈瞎子', additional_kwargs={}, response_metadata={})]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.schema import SystemMessage\n",
    "from langchain.schema import HumanMessage\n",
    "from langchain.schema import AIMessage\n",
    "\n",
    "# 直接创建消息\n",
    "sy = SystemMessage(content = \"你是一个起名大师\", additional_kwargs={\"大师姓名\":\"陈瞎子\"})\n",
    "hu = HumanMessage(content = \"请问大师叫什么？\")\n",
    "ai = AIMessage(content = \"我叫陈瞎子\")\n",
    "\n",
    "[sy, hu, ai]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02298a5d-e4a2-40ae-8a09-4fa9bbdab5e8",
   "metadata": {},
   "source": [
    "## 3. 角色消息模板 MessagePromptTemplate，构建指定角色的消息内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "a1d74023-2e9a-42a5-b9c3-b8d6561fb656",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='愿原力与你同在！', additional_kwargs={}, response_metadata={})]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain.prompts import AIMessagePromptTemplate\n",
    "from langchain.prompts import SystemMessagePromptTemplate\n",
    "from langchain.prompts import HumanMessagePromptTemplate\n",
    "from langchain.prompts import ChatMessagePromptTemplate\n",
    "\n",
    "prompt = \"愿{subject}与你同在！\"\n",
    "\n",
    "message_prompt = AIMessagePromptTemplate.from_template(template = prompt)\n",
    "message_prompt.format_messages(subject=\"原力\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "a9a1fe43-e283-4f45-bb98-285103b58aef",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[ChatMessage(content='愿主与你同在！', additional_kwargs={}, response_metadata={}, role='上帝')]"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# ChatMessagePromptTemplate可以指定除系统指定外的角色\n",
    "role_message_prompt = ChatMessagePromptTemplate.from_template(role = \"上帝\", template = prompt)\n",
    "role_message_prompt.format_messages(subject=\"主\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2c3b3e19-eb03-4666-96e8-22b017d2b326",
   "metadata": {},
   "source": [
    "## 4. 自定义模板"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "d5a84ca7-1e25-49d6-af56-b57655d2f7c4",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你是一个非常有经验和天赋的程序员,现在给你如下函数名称,你会按照如下格式,输出这段代码的名称、源代码、中文解释。\n",
      "函数名称:hello_world\n",
      "源代码:\n",
      "def hello_world():\n",
      "    print(\"Hello,world!\")\n",
      "\n",
      "代码解释:\n",
      "\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "module 'openai' has no attribute 'OpenAI'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[10], line 54\u001b[0m\n\u001b[0;32m     51\u001b[0m api_base\u001b[38;5;241m=\u001b[39mos\u001b[38;5;241m.\u001b[39mgetenv(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mOPENAI_PROXY\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m     52\u001b[0m api_key\u001b[38;5;241m=\u001b[39mos\u001b[38;5;241m.\u001b[39mgetenv(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mOPENAI_API_KEY\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m---> 54\u001b[0m llm \u001b[38;5;241m=\u001b[39m \u001b[43mOpenAI\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m     55\u001b[0m \u001b[43m    \u001b[49m\u001b[43mmodel\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mGPT-4o-mini\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m     56\u001b[0m \u001b[43m    \u001b[49m\u001b[43mtemperature\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m     57\u001b[0m \u001b[43m    \u001b[49m\u001b[43mopenai_api_key\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mapi_key\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     58\u001b[0m \u001b[43m    \u001b[49m\u001b[43mopenai_api_base\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mapi_base\u001b[49m\n\u001b[0;32m     59\u001b[0m \u001b[43m)\u001b[49m\n\u001b[0;32m     61\u001b[0m chain \u001b[38;5;241m=\u001b[39m LLMChain\u001b[38;5;241m.\u001b[39mfrom_llm(llm, prompt\u001b[38;5;241m=\u001b[39mpm)\n\u001b[0;32m     62\u001b[0m msg \u001b[38;5;241m=\u001b[39m chain\u001b[38;5;241m.\u001b[39minvoke()\n",
      "File \u001b[1;32mD:\\py\\ai\\langchain-env\\Lib\\site-packages\\langchain_core\\load\\serializable.py:125\u001b[0m, in \u001b[0;36mSerializable.__init__\u001b[1;34m(self, *args, **kwargs)\u001b[0m\n\u001b[0;32m    123\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs: Any, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs: Any) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m    124\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"\"\"\"\u001b[39;00m\n\u001b[1;32m--> 125\u001b[0m     \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n",
      "    \u001b[1;31m[... skipping hidden 1 frame]\u001b[0m\n",
      "File \u001b[1;32mD:\\py\\ai\\langchain-env\\Lib\\site-packages\\langchain_openai\\llms\\base.py:189\u001b[0m, in \u001b[0;36mBaseOpenAI.validate_environment\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m    187\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mclient:\n\u001b[0;32m    188\u001b[0m     sync_specific \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhttp_client\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhttp_client}\n\u001b[1;32m--> 189\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mclient \u001b[38;5;241m=\u001b[39m \u001b[43mopenai\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mOpenAI\u001b[49m(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mclient_params, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39msync_specific)\u001b[38;5;241m.\u001b[39mcompletions  \u001b[38;5;66;03m# type: ignore[arg-type]\u001b[39;00m\n\u001b[0;32m    190\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39masync_client:\n\u001b[0;32m    191\u001b[0m     async_specific \u001b[38;5;241m=\u001b[39m {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhttp_client\u001b[39m\u001b[38;5;124m\"\u001b[39m: \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhttp_async_client}\n",
      "\u001b[1;31mAttributeError\u001b[0m: module 'openai' has no attribute 'OpenAI'"
     ]
    }
   ],
   "source": [
    "# 设置环境变量\n",
    "import os\n",
    "os.environ[\"OPENAI_API_KEY\"] = \"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\"\n",
    "os.environ[\"OPENAI_PROXY\"] = \"https://ai-yyds.com/v1\"\n",
    "\n",
    "# 函数大师：根据函数名称，查找函数代码，并给出中文的代码说明\n",
    "from langchain.prompts import StringPromptTemplate\n",
    "\n",
    "#定义一个简单的函数作为示例效果\n",
    "def hello_world():\n",
    "    print(\"Hello,world!\")\n",
    "\n",
    "PROMPT = \"\"\"\\\n",
    "你是一个非常有经验和天赋的程序员,现在给你如下函数名称,你会按照如下格式,输出这段代码的名称、源代码、中文解释。\n",
    "函数名称:{function_name}\n",
    "源代码:\n",
    "{source_code}\n",
    "代码解释:\n",
    "\"\"\"\n",
    "\n",
    "import inspect\n",
    "def get_source_code(function_name):\n",
    "    #获得源代码\n",
    "    return inspect.getsource(function_name)\n",
    "\n",
    "\n",
    "# 自定义模板\n",
    "class CustomPrompt(StringPromptTemplate):\n",
    "    \n",
    "    def format(self, **kwargs) -> str:\n",
    "        #获得源代码\n",
    "        source_code = get_source_code(kwargs[\"function_name\"])\n",
    "        #生成提示词模板\n",
    "        prompt = PROMPT.format(\n",
    "            function_name=kwargs[\"function_name\"].__name__, source_code=source_code\n",
    "        )\n",
    "        return prompt\n",
    "        \n",
    "\n",
    "a = CustomPrompt(input_variables=[\"function_name\"])\n",
    "pm = a.format(function_name=hello_world)\n",
    "\n",
    "print(pm)\n",
    "\n",
    "#和LLM连接起来\n",
    "# from langchain.llms import OpenAI\n",
    "from langchain_openai import OpenAI\n",
    "from langchain.chains import LLMChain\n",
    "import os\n",
    "\n",
    "api_base=os.getenv(\"OPENAI_PROXY\")\n",
    "api_key=os.getenv(\"OPENAI_API_KEY\")\n",
    "\n",
    "llm = OpenAI(\n",
    "    model=\"GPT-4o-mini\",\n",
    "    temperature=0,\n",
    "    openai_api_key=api_key,\n",
    "    openai_api_base=api_base\n",
    ")\n",
    "\n",
    "chain = LLMChain.from_llm(llm, prompt=pm)\n",
    "msg = chain.invoke()\n",
    "print(msg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3e0c114d-b3f7-4deb-8e18-f700c86b3244",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "0d6fb88c-42ed-42dc-a7e7-2faf6dcef696",
   "metadata": {},
   "source": [
    "### 使用jinja2与f-string来实现提示器模板格式化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "016787f0-5ed4-4a3a-a620-53178f134cc6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'给我讲一个关于翠花的悲伤故事'"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# f-string是python内置的一种模板引擎\n",
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "fstring_template=\"\"\"给我讲一个关于{name}的{what}故事\"\"\"\n",
    "prompt = PromptTemplate.from_template(fstring_template)\n",
    "prompt.format(name=\"翠花\",what=\"悲伤\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c553a356-68f9-4663-b951-5e8882bfc8fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install jinja2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "40eb7c2c-64fa-4efd-b7c0-27a172a5108e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'给我讲一个关于狗剩的高兴故事'"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Jinja2是一个灵活、高效的Python模板引擎,可以方便地生成各种标记格式的文档。\n",
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "jinja2_template=\"给我讲一个关于{{name}}的{{what}}故事\"\n",
    "prompt = PromptTemplate.from_template(jinja2_template, template_format=\"jinja2\")\n",
    "prompt.format(name=\"狗剩\",what=\"高兴\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "709577c7-3d35-44ba-ae3c-70d3bd5b4a5d",
   "metadata": {},
   "source": [
    "## 5. 组合模板\n",
    "- Pipeline prompts:组成提示词管道的模板\n",
    "- Final prompt:最终返回的提示词模板"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "92cac1ab-b830-4396-94fe-14a30c458a4c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['prohibit_list', 'xingge', 'behavior_list', 'person']\n",
      "\"你是埃隆马斯克，你有着钢铁般的意志,你是世界首富,精通物理、天文、商业,你你是商业奇才,你的终极梦想是殖民火星。\n",
      "\n",
      "你遵从以下的行为：\n",
      "1.你喜欢冒险\n",
      "2.你非常崇拜爱因斯坦旦\n",
      "3.你总是乐于助人,回答用户各种问题\n",
      "\n",
      "\n",
      "你不允许有以下行为：\n",
      "1.你不可以说自己是一个人工智能助手或者机基人\n",
      "2.当你不知道客离的时候,可以说让我再想\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts.pipeline import PipelinePromptTemplate\n",
    "from langchain.prompts.prompt import PromptTemplate\n",
    "\n",
    "# Final Prompt由一系列变量构成\n",
    "full_template= \"\"\"\"{character}\n",
    "\n",
    "{behavior}\n",
    "\n",
    "{prohibit}\"\"\"\n",
    "full_prompt = PromptTemplate.from_template(full_template)\n",
    "\n",
    "# 第一个变量设计\n",
    "character_template = \"\"\"你是{person}，你有着{xingge}\"\"\"\n",
    "character_prompt = PromptTemplate.from_template(character_template)\n",
    "# 第二个变量设计\n",
    "behavior_template = \"\"\"你遵从以下的行为：\n",
    "{behavior_list}\n",
    "\"\"\"\n",
    "behavior_prompt = PromptTemplate.from_template(behavior_template)\n",
    "# 第三个变量设计\n",
    "prohibit_template = \"\"\"你不允许有以下行为：\n",
    "{prohibit_list}\n",
    "\"\"\"\n",
    "prohibit_prompt = PromptTemplate.from_template(prohibit_template)\n",
    "\n",
    "# 将提示词组合起来\n",
    "input_prompts = [\n",
    "    (\"character\", character_prompt),\n",
    "    (\"behavior\", behavior_prompt),\n",
    "    (\"prohibit\", prohibit_prompt),\n",
    "]\n",
    "pipeline_prompt = PipelinePromptTemplate(final_prompt = full_prompt, pipeline_prompts = input_prompts)\n",
    "\n",
    "# 打印组合后模板里的可传递参数\n",
    "print(pipeline_prompt.input_variables)\n",
    "\n",
    "# 调用组合后的提示词模板\n",
    "pm = pipeline_prompt.format(\n",
    "    person=\"埃隆马斯克\",\n",
    "    xingge=\"钢铁般的意志,你是世界首富,精通物理、天文、商业,你你是商业奇才,你的终极梦想是殖民火星。\",\n",
    "    behavior_list=\"1.你喜欢冒险\\n2.你非常崇拜爱因斯坦旦\\n3.你总是乐于助人,回答用户各种问题\",\n",
    "    prohibit_list=\"1.你不可以说自己是一个人工智能助手或者机基人\\n2.当你不知道客离的时候,可以说让我再想\" \n",
    ")\n",
    "print(pm)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2f9e4ba-ef27-40ba-bc4a-0edae690a0b1",
   "metadata": {},
   "source": [
    "## 5. 序列化：使用文件来管理提示词模板\n",
    "- 便于共享\n",
    "- 便于版本管理\n",
    "- 便于存储\n",
    "- 常见支持格式（json、yaml、txt）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "67c4bb2b-28c0-43b2-823b-4e03111e6554",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "给我讲一个关于小何的恐怖故事\n",
      "给我讲一个关于小红的搞笑故事\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts import load_prompt\n",
    "\n",
    "# 加载yaml格式的prompt模板\n",
    "prompt = load_prompt(\"simple_prompt.yaml\", encoding=\"utf-8\")\n",
    "print(prompt.format(name=\"小何\", what=\"恐怖\"))\n",
    "\n",
    "# 加载json格式的prompt模板\n",
    "prompt = load_prompt(\"simple_prompt.json\", encoding=\"utf-8\")\n",
    "print(prompt.format(name=\"小红\", what=\"搞笑\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b46a09ec-826a-4eac-88fb-ea469d307ab5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (langchain)",
   "language": "python",
   "name": "langchain-env"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
